/**
 * Copyright (C)  Gadglet .
 *
 * This file is part of Gadglet
 *
 * Gadglet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Gadglet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Gadglet. If not, see <http://www.gnu.org/licenses/>.
 */

package com.gadglet.client.gwt.core;

import java.util.ArrayList;
import java.util.Map;

import com.gadglet.client.gwt.GadgetUtils;
import com.gadglet.client.gwt.core.ui.UserRegistration;
import com.gadglet.client.gwt.ui.DebugDialogBox;
import com.gadglet.client.gwt.ui.RequestLoadingImage;
import com.gadglet.client.gwt.ui.SimpleDialogBox;
import com.gadglet.params.ReqActionTypes;
import com.gadglet.params.ReqErrorTypes;
import com.gadglet.params.ReqTypes;
import com.gadglet.params.SharedConstants;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.gadgets.client.PreferencesFeature;
import com.google.gwt.gadgets.client.PreferencesProvider;
import com.google.gwt.gadgets.client.io.AuthorizationType;
import com.google.gwt.gadgets.client.io.ContentType;
import com.google.gwt.gadgets.client.io.GadgetsIo;
import com.google.gwt.gadgets.client.io.IoProvider;
import com.google.gwt.gadgets.client.io.MethodType;
import com.google.gwt.gadgets.client.io.Response;
import com.google.gwt.gadgets.client.io.ResponseReceivedHandler;


/**
 * 		GadgletRequest perform the request to the gadget server using the GadgletQuery. The class
 * 		handles: registration, OAuth authorization, missing profile and more.. 
 * 			
 *  	 In order to use, a developer must extend this class and
 *       implement the abstract method processResults. to execute a request
 *       use the method makeRequest
 */

public  class GadgletRequest {

	final private ContentType contentType = ContentType.JSON;
	final SimpleDialogBox simpleDialog = SimpleDialogBox.getMesseageDialogBox();

	final PreferencesFeature  prefsUtils = PreferencesProvider.get();

	private MethodType methodType = MethodType.GET;
	private ArrayList<Headers> headersList = new ArrayList<Headers>();
	final protected DebugDialogBox errorNotifier = DebugDialogBox
	.getErrorNotifier();
	final protected RequestLoadingImage loading = RequestLoadingImage
	.getContentLoadingImage();
	final GadgletRequestOptions options =  GadgletRequestOptions
	.newInstance().cast();

	private GadgletQuery requestQry = null;

	private boolean ignoreErrors = false;
	
	
	public boolean isIgnoreErrors() {
		return ignoreErrors;
	}

	
	/**
	 * @param ignoreErrors true - Gadget developer is responsible on exception handling
	 * false (default)- GadgletRequest  is responsible on exception handling.
	 */
	public void setIgnoreErrors(boolean ignoreErrors) {
		this.ignoreErrors = ignoreErrors;
	}

	public GadgletRequest(GadgletQuery req) {
		this.requestQry = req;
	}

	@SuppressWarnings("unused")
	private GadgletRequest() {

	}
	
	
	/**
	 * @param methodType - http method type
	 */
	public void setMethodType (MethodType methodType)
	{
		this.methodType = methodType;
	}
	
	

	/**
	 * This method should be @deprecated on the next version
	 */
	public boolean makeRequest() {

		return makeRequest(null);
	
	}
	
	/**
	 * This method is used to perform the request. The method will handle the
	 * following cases: 1. OAuth authorization if needed 2. Users
	 * registration 3. General Errors 4. In case the results are without errors
	 * the method processResults will be called
	 */
	
	public boolean makeRequest(final ResponseHandler handler){
		String url = requestQry.getRequestURL();

		if (requestQry.getRequestType() == ReqTypes.BIZLET_OAUTH) 
		{
			options.setUseToken("always");
			options.setAuthorizationType(AuthorizationType.OAUTH);
			options.setServiceName(SharedConstants.gadgletOauthServiceName);
		}
		else 
		{
			options.setUseToken("never");
			options.setAuthorizationType(AuthorizationType.SIGNED);
		}
	
		options.setRefreshInterval(1);
		options.setContentType(contentType);
		options.setMethodType(methodType);
		
		if(methodType.equals(MethodType.POST)){
		
			options.setPostData(url.substring(url.indexOf("?")+1));
			url = url.substring(0,url.indexOf("?"));
		
		}
					
		for (Headers header : headersList)
			options.setHeader(header.getName(), header.getValue());

		GadgetsIo gIO = IoProvider.get();
		loading.start();
		gIO.makeRequestAsJso(url,
				new ResponseReceivedHandler<JsArray<JavaScriptObject>>() {
			@Override
			public void onResponseReceived(
					ResponseReceivedEvent<JsArray<JavaScriptObject>> event) {
				Response<JsArray<JavaScriptObject>> response = event
				.getResponse();
				loading.stop();
				// some unknown issue
				if (response == null) {
					errorNotifier
					.showError(11, "error: response = null");
				}
				// successful request
				else if (response.getStatusCode() == 200
						&& response.getData() != null) {
					GadgletResponse jResponse = new GadgletResponse(response
							.getData());

					// handle all response except registration
					
					if(jResponse.isSuccessful() || isIgnoreErrors()){
						
						if(handler == null)
							processResults(jResponse);
						else 
							{
							 if(jResponse.isSuccessful())
								 handler.onSuccess(jResponse);
							 else
								 handler.onError(jResponse);
							}
					}
				 	else if (jResponse.getError().equals(
							ReqErrorTypes.PROFILE_DOESNT_EXISTS
							.getErrorMessage())){
						if(!getGadgletRequest().getRequestQry().getRequestAction().equals(ReqActionTypes.GET_PROFILE)){
							String  profileImage = GadgetUtils.getIconURL("profile", null, false);
							simpleDialog.showMessage(prefsUtils.getMsg(ReqErrorTypes.PROFILE_DOESNT_EXISTS
									.getErrorMessage())+"&nbsp;<img src='"+profileImage+"'></img>");
						}
						else
							processResults(jResponse);

					}
					else if (jResponse.getError().equals(
							ReqErrorTypes.USER_NOT_REGISTERED
							.getErrorMessage())) {
						// Google Apps user registration
						String regToken = "";
						Map<String, String> errors = jResponse
						.getFieldsErrors();

						// the registration to Gadgets for google Apps
						// includes 2 requests: one OAuth, One Signed.
						// in case the admin choose only one request
						// (Signed) in the context param "IdentityCheck"
						// the server will handle that, with the right
						// params
						if (errors != null
								&& errors
								.get(SharedConstants.registrationProcessParamName)
								.equals(SharedConstants.registrationDoOAuth)) {
							getRequestQry().setRequestType(
									ReqTypes.BIZLET_OAUTH);
							if (errors
									.get(SharedConstants.registrationTokenParamName) != null)
								getRequestQry()
								.addParam(
										SharedConstants.registrationTokenParamName,
										errors.get(SharedConstants.registrationTokenParamName));
							makeRequest();
						} else if (errors != null
								&& errors
								.get(SharedConstants.registrationProcessParamName)
								.equals(SharedConstants.registrationDoSigned)) {
							getRequestQry().setRequestType(
									ReqTypes.BIZLET_SIGNED);
							if (errors
									.get(SharedConstants.registrationTokenParamName) != null)
								getRequestQry()
								.addParam(
										SharedConstants.registrationTokenParamName,
										errors.get(SharedConstants.registrationTokenParamName));
							makeRequest();
						} else if (errors != null
								&& errors
								.get(SharedConstants.registrationTokenParamName) != null) {
							regToken = errors
							.get(SharedConstants.registrationTokenParamName);

							String registrationUrl = GadgetUtils
							.getGadgetserver(true)
							+ SharedConstants.secureDirName
							+ SharedConstants.userRegName
							+ SharedConstants.registrationTokenParamName
							+ "=" + regToken;
							  new UserRegistration().register(registrationUrl,
									getGadgletRequest(),handler);
						}

					} else
						{
						if(handler == null)
							processResults(jResponse);
						else 
							{
							 if(jResponse.isSuccessful())
								 handler.onSuccess(jResponse);
							 else
								 handler.onError(jResponse);
							}
						}
				}
				// OAuth errors
				else if (response.getStatusCode() != 200
						&& response.getOauthError() != null) {
					simpleDialog.showError(prefsUtils
							.getMsg("gadgetMsgApproveError"));
					errorNotifier.showError(44, response.getOauthError()
							+ response.getOauthErrorText());
				}
				// handle OAuth authorization
				else if (response.getStatusCode() == 200
						&& response.getOauthApprovalUrl() != null) {

					new UserRegistration().approveOAuth(
							response.getOauthApprovalUrl(),
							getGadgletRequest(),handler);
				}

				else {
					simpleDialog.showError(prefsUtils
							.getMsg("gadgetMsgApproveError"));
				}
			}

		}, options);

		return true;
	}

	public  GadgletQuery getRequestQry() {
		return requestQry;
	}

	private GadgletRequest getGadgletRequest() {
		return this;
	}

	/**
	 * This method should be implemented by the Gadget developer
	 * in case he doesn't want to use makeRequest(ResponseHandler)
	 * 
	 * @param data   - A GadgletResponse object represent the results from the server
	 */
	protected  void processResults(GadgletResponse data){
		
	}

	/**
	 * Method that enables developer to add custom headers
	 * 
	 * @param name
	 * @param value
	 */
	public void setHeaders(String name, String value) {
		Headers item = new Headers(name, value);
		headersList.add(item);
	}

	private class Headers {
		public String getName() {
			return name;
		}

		public String getValue() {
			return value;
		}

		private String name;
		private String value;

		Headers(String name, String value) {
			this.name = name;
			this.value = value;
		}
	}

}
